#include "key.h"

#include "data.h"
#include "dict.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <err.h>

#include <mcheck.h>

#include "./lib/parStr.h"
#include "./lib/cr.h"
#include "./lib/cw.h"

struct sword {
  key k;
  data d;
};


struct sdict {
  word *w;      
  int cant;    
};



/* ------------------------------------------------- */

dict 
dict_empty(void)
{
  dict dr;
  
  dr=(dict)malloc(sizeof(struct sdict));

  dr->w=(word *)malloc(sizeof(struct sword));
  dr->cant=0;


  return dr;
}

/*
  DESC: Constructor del tipo. Crea un diccionario vacio.
  PRE: { d = NULL }
  d = dict_empty(f);
  POS: {  Hay memoria  ==> d --> empty
  ! Hay memoria  ==> d == NULL }
*/


dict
dict_fromFile(char * f)
{

  /*creo un array que contenga los elementos del archivo. */
  
  char *kk;
  char *dd;
  cr c1;
  parStr e;

  dict dicc=dict_empty();
  key kkk;
  data ddd;
  

  c1 = cr_create(f);

  /* No crea el archivo*/
  if (c1 == NULL)
    {
      errx(EXIT_FAILURE, "Error al crear cinta de lectura"
	   ", archivo %s, linea %i", __FILE__, __LINE__);
    }
  
  /* Arranca la cinta de lectura*/
  c1 = cr_arr(c1);
  if (c1 == NULL)
    {
      errx(EXIT_FAILURE, "Error al arrancar cinta de lectura"
	   ", archivo %s, linea %i", __FILE__, __LINE__);
    }
  
  
  /* Carga los valores contenidos en el diccionario*/
  
  while (c1 != NULL && !cr_fin(c1)) /* Si c1 es NULL es porque no pudo arrancar la cinta */
    {
      
      /* Carga la primera linea*/
      e = cr_elecor(c1);
      
      /* Si no pudo cargar la primera linea*/
      
      if (e == NULL)
	{
	  errx(EXIT_FAILURE, "No se devolvio elecorr"
	       ", archivo %s, linea %i", __FILE__, __LINE__);
	}
      
      kk=parStr_fst(e); 
      dd=parStr_snd(e); 
      kkk=key_empty();
      ddd=data_empty();
      if (kk == NULL || dd == NULL)
	{
	  errx(EXIT_FAILURE, "no se devolvio algun string" 
	       ", archivo %s, linea %i", __FILE__, __LINE__); 
	}
      
      /* Agregar los datos al array */
      assert(key_copyStr(kkk,kk));
      assert(data_copyStr(ddd,dd));
      dict_add(dicc,kkk,ddd);
      
	/* Libera el contenido de las variables */
	free(kk);
	free(dd); 
	kkk=key_destroy(kkk);
	ddd=data_destroy(ddd);

  
      /*libera e*/
      e = parStr_destroy(e);
      
      /*da el avance de linea*/
      cr_av(c1);
    }
  
  if (c1!=NULL)
    {
      c1 = cr_destroy(c1);	
    }
  
  return dicc;
  
}

/*
  DESC: Constructor del tipo. Crea un diccionario a partir de un archivo.
  Toma el nombre del archivo.
  PRE: { d = NULL }
  d = dict_fromFile(f);
  POS: {  Hay memoria /\ ! Error de Archivo ==> d --> fromFile(f) 
  ! Hay memoria \/   Error de Archivo ==> d == NULL }
*/

dict 
dict_destroy(dict d)
{
  int i;
  
  for(i=0;i<(d->cant);i++)
    {
      if ((((d->w)[i]).k)!=NULL)
	(((d->w)[i]).k)=key_destroy(((d->w)[i]).k);
      if ((((d->w)[i]).d)!=NULL)
	(((d->w)[i]).d)=data_destroy(((d->w)[i]).d);

      assert((((d->w)[i]).k)==NULL);
      assert((((d->w)[i]).d)==NULL);
	
    }
  

  
  free(d->w);        /*Liberamos el arreglo de la estructura word*/
  (d->w)=NULL;       /* Desapuntar el puntero al arreglo */
  assert(d->w==NULL);
  
  free(d);           /*Liberamos cant y el puntero al arreglo*/
  d=NULL;
  assert(d==NULL);
  
  return d;
}

/*
  DESC: Destructor del tipo. d cambia.
  PRE: { d --> D }
  d = dict_destroy(d);
  POS: { Se destruye D /\ d == NULL }
*/

bool
dict_toFile(char *f , const dict d)
{
  cw c2;
  parStr e;
  int i=0;
  bool b;
  char *str1;
  char *str2;
  /*crea la cinta*/
  c2=cw_create(f);
  
  if (c2 == NULL)
    {
      errx(EXIT_FAILURE, "Error al crear cinta de escritura" 
	   ", archivo %s, linea %i", __FILE__, __LINE__);
    }
  
  /*arranca la cinta*/
  c2=cw_arr(c2);
  
  if(c2 == NULL)
    {
      errx(EXIT_FAILURE, "Error al arrancar cinta de escritura"
	   ", archivo %s, linea %i", __FILE__, __LINE__);
    }
  
  while ( i<(d->cant) && c2 != NULL)
    {
      str1=key_toStr((d->w)[i].k);
      str2=data_toStr((d->w)[i].d);
      e = parStr_create(str1,str2);
    
   
      
      if (e == NULL)
	{
	  errx(EXIT_FAILURE, "No se devolvio elecor"
	       ", archivo %s, linea %i", __FILE__, __LINE__);
	}
	  
      
      c2 = cw_insert(c2, e);
      
      e = parStr_destroy(e);
      i = i+1;
      
      free(str1);
      free(str2);
    }
	
	b = (c2 != NULL);
	if (b)	
	    c2 = cw_destroy(c2);
	
  return b;
}

/*
  DESC: Guarda el diccionario en el archivo f.
  PRE: { d --> D }
  b = dict_toFile(f, d);
  POS: { ! Error de Archivo ==>  b /\ D guardado en archivo f 
  Error de Archivo ==> !b }
*/


dict
dict_add(dict d, const key k, const data e)
{

  char *str1;
  char *str2;

      d->w=(word *)realloc(d->w,sizeof(struct sword)*((d->cant)+1));
	

      ((d->w)[d->cant]).k=key_empty();  /* Inicializamos el espacio para la palabra*/
      ((d->w)[d->cant]).d=data_empty(); /* Inicializamos el espacio para la definicion*/
      
      /* Agregamos la palabra */
  
      str1=key_toStr(k);
      key_copyStr(((d->w)[d->cant]).k,str1);
      
      /* Agregamos la definicion */
      str2=data_toStr(e);
      data_copyStr(((d->w)[d->cant]).d,str2);


      /* Agrandador de diccionario y liberador de str1 y str2*/
      d->cant=(d->cant)+1;

      free(str1);
      free(str2);
     
  return d;
}

/*
  DESC: Agrega en el diccionario el dato d con la clave k.
  Si la clave ya existe no hace nada.
  Si agrega no utiliza k ni e (copia).
  PRE: { d --> D /\ k --> K /\ e --> E /\ !exist(D,K)}
  d = dict_add(d, k, e);
  POS: { d --> add(D,K,E) }
*/

bool
dict_exist(const dict d, const key k)
{
  if (d->cant==0)
	return FALSE;
  else
  	return (dict_search(d,k)!=NULL);
}

/*
  DESC: Busca el dato con clave k si existe. Si no existe devuelve False.
  PRE: { d --> D /\ k --> K }
  b = dict_exist(d, k);
  POS: { (D =  add(D',K,E) /\ b ) \/ 
  (D != add(D',K,E) /\ !b ) }
*/

data
dict_search(const dict d, const key k)
{
  
  int i;

  
  i=lsearch(d,k);
   
	if (i!=-1)
	    return ((d->w)[i]).d;
	else
	    return NULL;
 
}


/*
  DESC: Busca y trae el dato con clave k si existe. Si no devuelve NULL.
  Crea copia del dato.
  PRE: { d --> D /\ k --> K /\ e = NULL }
  e = dict_search(d, k);
  POS: { (D =  add(D',K,E) /\ e --> E ) \/ 
  (D != add(D',K,E) /\ e = NULL ) }
*/

dict
dict_del(dict d, const key k)
{
  
  int c;

  
  c=lsearch(d,k);
  
  if(c!=-1)
  {   
      ((d->w)[c]).k=key_destroy(((d->w)[c]).k);
      ((d->w)[c]).d=data_destroy(((d->w)[c]).d);

      assert((((d->w)[c]).k)==NULL);
      assert((((d->w)[c]).d)==NULL);
  
      dic_swap(d,c,(d->cant)-1);
  
      (d->cant)=(d->cant)-1;
  }

  return d;
}

/*
  DESC: Borra el dato con clave k. Si no esta devuelve null. 
  Destruye contenidos.
  PRE: { d --> D /\ k --> K /\ e = NULL }
  d = dict_del(d, k);
  POS: { ( D  = add(D',K,E) /\ d --> D') \/
  ( D != add(D',K,E) /\ d --> D }
*/

int
dict_length(dict d)
{
  return (d->cant);
}

/*
  DESC: Devuelve la cantidad de estradas en el diccionario. 
  PRE: { d --> D }
  l = dict_length(d);
  POS: { l = length(D) }
*/


int
lsearch(dict d, key k)
{
  int i=0;
  char *str1;
  char *str2;
 
	while (i<d->cant) 
	{
	  str1=key_toStr(k);
	  str2=key_toStr(((d->w)[i]).k);
	  if (strcmp(str1,str2)==0){
	    free(str1);
	    free(str2);
	    return i;
	  }
	  i=i+1;
	  free(str1);
	  free(str2);
	  
	}
assert((((d->w)[i-1]).k)!=NULL);

  return (-1);

/*regresa -1 si no se encontro, si no regresa la posicion de del key encontrado*/

}

void
listado(dict d)
{
int i=0;
 char *str1;
system("clear");
printf("\n------------------------------------------------------");
printf("\n---------------------INICIO---------------------------");
printf("\n------------------------------------------------------");
	while (i<d->cant) 
	{
	  str1=key_toStr(((d->w)[i]).k);
		printf("\n%s",str1);
		i=i+1;
		free(str1);
	}
printf("\n------------------------------------------------------");
printf("\n-----------------------FIN----------------------------");
printf("\n------------------------------------------------------\n\n");
}

void
dic_swap(dict d, int a, int b)
{

  word temp;

  temp=(d->w)[a];
  (d->w)[a]=(d->w)[b];
  (d->w)[b]=temp;

  
}
